package cn.tedu._07springaop.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Aspect
@Component
public class LogAspect {


    /**
     * @Pointcut注解
     * 1、定义切点
     * 2、定义切点表达式
     * 3、细粒度的切点（execution()）
     * 4、粗粒度的切点
     *  4.1 bean(bean的名字（类名）)
     *  4.2 within(包名.类名)
     *
     */
    @Pointcut("execution(public int cn.tedu._07springaop.aop.CalculatorImpl.*(..))")
    public void doTime(){}

    /**
     * @Before注解
     * 1、前置通知。方法开始前执行
     */
    @Before("doTime()")
    public void beforeMethod(JoinPoint joinPoint) {
        String name = joinPoint.getSignature().getName();
        String args = Arrays.toString(joinPoint.getArgs());
        System.out.println("[前置通知]"+name+"方法开始了，参数为："+args);
    }


    /**
     * @After注解
     * 1、后置通知。最后打印
     */
    @After("doTime()")
    public void afterMethod(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("[后置通知]"+methodName+"方法结束了");
    }

    /**
     * @AfterReturning注解
     * 1、返回通知。方法正常结束后执行
     * 2、返回通知有权限获取到目标方法的返回值
     */
    @AfterReturning(value = "doTime()",returning = "result")
    public void afterReturning(JoinPoint joinPoint,Object result) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("[返回通知]"+methodName+"方法正常返回，结果为："+result);
    }

    /**
     * @AfterThrowing注解
     * 1、异常通知。方法抛异常时执行
     * 2、异常通知有权限获取到目标方法抛出的异常
     */
    @AfterThrowing(value = "doTime()",throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint,Throwable ex) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("[异常通知]"+methodName+"方法异常，异常信息为："+ex.getMessage());
    }

    /**
     * @Around注解
     * 1、环绕通知。方法开始时执行，方法正常结束后执行，方法异常时执行
     * 2、环绕通知没有权限获取到目标方法的返回值
     */
    @Around(value = "doTime()")
    public Object aroundMethod(ProceedingJoinPoint joinPoint){
        Object result = null;
        /*
            try {
                1.前置通知;
                2.执行目标方法;
                3.返回通知;
            }catch (Throwable ex) {
                4.异常通知;
            }finally {
                5.后置通知;
            }
         */
        try {
            System.out.println("[环绕通知]:前置通知");
            result = joinPoint.proceed();
            System.out.println("[环绕通知]:返回通知");
        } catch (Throwable e) {
            System.out.println("[环绕通知]:异常通知");
        } finally {
            System.out.println("[环绕通知]:后置通知");
        }
        return result;
    }
}
